.. _глава символы:

Символы
=======

.. index::
    Символы

Символы есть строительными блоками строк. Символом называется любой символ системы письма: буквы алфавитов, цифры,
знаки препинания, символ пробела и т.д. Как ни странно, строительные блоки самих символов также называются символами.
Массивы символов образуют строки. Мы рассматривали массивы в предыдущей главе; строки будут рассмотрены двумя главами позже.
Как и любые другие данные, символы также кодируются целочисленными значений. Например, целочисленное значение строчной буквы "a" равно 97, а
целочисленное значение цифры "1" равно 49. Эти значения являются просто условным обозначением, присвоенным при разработке стандарта ASCII.
Во многих языках программирования символы представлены типом char, который может содержать только 256 различных значений. Если вы знакомы с типом char
из других языков, вы, возможно, уже знаете, что он недостаточно велик для
поддержки символов разных языков. Прежде чем перейти к трем различным
типам символов языка D, давайте сначала взглянем на историю символов в компьютерных системах.

История
--------

.. index::
    ASCII Table
    IBM Code Pages
    ISO/IEC 8859
    Unicode


**ASCII Table**

ASCII кодировка была разработана в то время,когда аппаратное обеспечение обладало минимальными ресурсами
по сравнению с современными системами. Коды ASCII имеют длину 7, поэтому изначально кодировка могла поддержать 128 различных символов
Этого количества различных значений достаточно для представления строчных и прописных версий 26 букв основного латинского
алфавита, цифр, обычно используемых знаков препинания и некоторых символов управления терминалом.
В качестве примера, ASCII-коды символов строки "hello" следующие (запятые вставлены просто для удобства чтения):
104, 101, 108, 108, 111ю.Каждый приведенный выше код представляет собой одну букву "hello". Например, есть два
значения 108, соответствующие двум буквам "l". Коды таблицы ASCII позже были увеличены до 8 бит, чтобы стать
Расширенная таблица ASCII. Расширенная таблица ASCII содержит 256 различных кодов.

**IBM Code Pages**

Корпорация IBM предоставила набор таблиц, каждая из которых присваивает коды
расширенной таблицы ASCII от 128 до 255 одной или нескольким системам записи. Эти
кодовые таблицы позволили поддерживать буквы многих других алфавитов. Например,
специальные буквы турецкого алфавита являются частью кодовой страницы IBM 857.
Несмотря на то, что кодовые страницы намного полезнее ASCII, у них есть некоторые проблемы
и ограничения: для корректного отображения текста необходимо знать, на какой кодовой
странице изначально был написан данный текст. Это связано с тем, что один и тот же код
соответствует другому символу в большинстве других таблиц. Например, код
, представляющий "ª" в таблице 857, соответствует "ª" в таблице 437.
В дополнение к трудностям с поддержкой нескольких алфавитов в одном
документе, алфавиты, содержащие более 128 символов, отличных от ASCII, вообще не могут поддерживаться таблицей IBM.


**ISO/IEC 8859 кодовые страницы**

Стандарт ISO/IEC 8859 кодовых страниц был результатом усилий по международной стандартизации.
Они похожи на кодовые страницы IBM в том, как они присваивают коды символам. В качестве
примера, специальные буквы турецкого алфавита указаны на кодовой странице 8859-9.
Эти таблицы имеют те же проблемы и ограничения, что и таблицы IBM.
Например, голландский диграф не фигурирует ни в одной из этих таблиц.


**Unicode**

Unicode разрешил все проблемы и ограничения предыдущих решений. Unicode
включает в себя более ста тысяч знаков и условных обозначений систем письма
многих человеческих языков, современных и старых. ((Новые постоянно
рассматриваются для добавления в таблицу.) Каждый из этих символов имеет уникальный
код. Документы, закодированные в Unicode, могут включать все символы
отдельных систем письма без какой-либо путаницы или ограничений.


**Кодировка Unicode**

.. index::
    UTF-32
    UTF-8
    UTF-16

Unicode присваивает уникальный код каждому символу. Поскольку
символов Unicode больше, чем может вместить 8-разрядное значение, некоторые символы должны быть представлены
как минимум двумя 8-разрядными значениями. Например, код символа в Юникоде "Ğ" (286)
больше максимального значения в один байт. Способ представления символов на электронных носителях называется
кодировкой. Ранее мы видели, как строка "hello" кодируется в ASCII.
Теперь мы увидим три кодировки Unicode, соответствующие типам символов языка D.

**UTF-32** : В этой кодировке используется 32 бита (4 байта) для каждого символа Unicode. То
Кодировка "hello" в UTF-32 аналогична кодировке ASCII, но каждый символ
представлен 4 байтами:

.. code-block:: console

    0,0,0,104, 0,0,0,101, 0,0,0,108, 0,0,0,108, 0,0,0,111

Другой пример UTF-32 кодировка для последовательности символов "aĞ" будет выглядеть так:

.. code-block:: console

    0,0,0,97, 0,0,1,30

*Примечание: Порядок байт кодировки UTF-32 может отличаться на разных компьютерных системах*

Последовательность символов 'a' и 'Ğ' представлены 1 и 2 значащими байтами соответственно,  все
значения остальных 5 байт являются нулями. Эти нули можно рассматривать как байты-заполнители,
чтобы каждый символ Unicode занимал по 4 байта каждый. Для документов, основанных на базовом латинском алфавите, в этой кодировке всегда используется в 4
раза больше байт, чем в кодировке ASCII. Когда большинство символов данного документа имеют эквиваленты ASCII, 3 байта заполнения для каждого из этих
символов делают эту кодировку более расточительной по сравнению с другими кодировками.
С другой стороны, есть преимущества представления каждого символа с помощью
равное количество байт. Например, следующий символ Unicode всегда находится на расстоянии ровно
четырех байт.

**UTF-16**: Эта кодировка использует 2 байта для представления большинства символов Юникода.
Поскольку 16 бит могут содержать около 65 тысяч уникальных значений, остальные (менее
часто используемые) 35 тысяч символов Юникода должны быть представлены с использованием
дополнительных байтов.В качестве примера, "aĞ" кодируется 4 байтами в UTF-16:

.. code-block:: d

    0,97, 1,30


По сравнению с  UTF-32, данная кодирвка экономит память для хранения большинства документов, but
because some characters must be represented by more than 2 bytes, UTF-16 is
more complicated to process
но поскольку часть символов требует для своего представления больше 2-ч байт,кодирование/декодирование
становится сложным процессом

**UTF-8**: В этой кодировке используется от 1 до 4 байт для каждого символа. Если символ имеет
эквивалент в таблице ASCII, он представлен 1 байтом с тем же числовым
кодом, что и в таблице ASCII. Остальные символы Unicode представлены
2, 3 или 4 байта. Большинство специальных символов европейских систем записи
относятся к группе символов, которые представлены 2 байтами.
Для большинства документов в западных странах UTF-8 - это кодировка, которая занимает
наименьшее количество места. Еще одним преимуществом UTF-8 является то, что документы, которые были
созданный с использованием ASCII, может быть открыт непосредственно (без преобразования) как UTF-8
документы. UTF-8 также не тратит впустую пространство на байты-заполнители, поскольку каждый
символ представлен значащими байтами. В качестве примера, кодировка UTF-8
для "aĞ" является:

.. code-block:: console

    97, 196,158

Символьные типы в языке D
-------------------------

.. index::
    char
    wchar
    dchar

В языке D присутствуют три типа для представления символов. Все типы соответвуют трём типа Unicode кодировки рассмотренные выше

.. list-table:: Title
   :widths: 25 25 50
   :header-rows: 1
   :align: center

   * - тип
     - диапазон
     - начальное значение
   * - char
     - кодовая единица UTF-8
     - 0xFF
   * - wchar
     - кодовая единица UTF-16
     - 0xFFFF
   * - dchar
     - кодовая единица UTF-32
     - 0x0000FFFF

В отличие от некоторых других языков программирования, символы в языке D могут состоять из
разного количества байт. Например, поскольку "Ğ" в Unicode должно быть представлено
как минимум 2 байтами, оно не помещается в переменную типа char . С другой
стороны, поскольку dchar состоит из 4 байт, он может содержать любой символ Unicode.

Символьные литералы
-------------------

Литералы - это константные значения, которые есть часть програмного кода. В D, символьные литералы специфицируются одиночными кавычками

.. code-block:: d

    char letter_a = 'a';
    wchar letter_e_acute = 'é';


Использование двойных кавычек недопустимо для символов, поскольку двойные кавычки используются при
указании строк, что мы увидим в следующей главе (стр. 75). 'a' - символьный
литерал, а "a" - строковый литерал, состоящий из одного символа.
Переменные типа char могут содержать только буквы, которые есть в таблице ASCII.

Существует множество способов вставки символов в код

#.  Наиболее естественным является ввод их с клавиатуры.
#.  Копирование из другой программы или другого текста. Например, вы можете скопировать и вставить с веб-сайта или из программы,
    специально предназначенной для отображения  Символов Юникода.
    (Одной из таких программ в большинстве сред Linux является    Character Map (карта символов в терминале).)
#.  Использование коротких имен символов. Синтаксис для этой функции -  \&character_name; .
    Например, название знака евро - euro, и оно     может быть указано в программе следующим образом
    Смотри список имен символов по ссылке http://dlang.org/entity.html

.. code-block:: d

    wchar currencySymbol = '\&euro;';

#.  Указывать для конкретного символа его целочисленный Unicode код:

.. code-block:: console

    char a = 97;
    wchar Ğ = 286;

#.  Указывать значение кодов из ASCII таблицы, используя либо восьмеричное либо шестнадцатиричное представление

.. code-block:: d

    char questionMarkOctal = '\77';
    char questionMarkHexadecimal = '\x3f';

#.  Указывать значений символов в Юникоде с помощью
    синтаксиса \ufour_digit_value для wchar и
    синтаксиса \Ueight_digit_value для dchar (обратите внимание на u против U ). Значения в Юникоде должны быть указаны в
    шестнадцатеричном формате:

.. code-block:: console

    wchar Ğ_w = '\u011e';
    dchar Ğ_d = '\U0000011e';

Данные методы также можно использовать для указания символов внутри строк.
Например, следующие две строки содержат одинаковые строковые литералы:

.. code-block:: d

    writeln("Résumé preparation: 10.25€");
    writeln("\x52\&eacute;sum\u00e9 preparation: 10.25\&euro;");

Управляющие символы
-------------------

.. index::
    Управляющие символы

Некоторые символя используются только для форматирования текста и не имеют визуального представления
Наппример символ перевода строки, который указывает, что дальнейший вывод должен идти с новой  строки.
Подобные символы называются управляющими символами. Некоторые управляющие символы могут быть представлены через синтаксис : \control_character syntax.
то есть с экранированием.

.. list-table:: Title
   :widths: 25 25 50
   :header-rows: 1
   :align: center

   * - Синтаксис
     - Название
     - Определение
   * - \\n
     - новая строка
     - Перевод вывода на новую строку
   * - \\r
     - перевод каретки
     - Перевод вывода с начала текущей строки
   * - \\t
     - табуляция
     - Вставить в текущую строку символ табуляции

Например, функция write(), которая не переходит на новую строкутоматически, но
будет делать это для каждого символа \n. Каждое появление управляющего символа \n
в следующем литерале представляет начало новой строки:

.. code-block:: d

    write("first line\nsecond line\nthird line\n");

Вывод

.. code-block::  console

    first line
    second line
    third line


Одиночные кавычка и обратный слэш
----------------------------------

.. index::
    ''
    ""
    \\

Одиночная кавычка (''')сама по себе записана внутри одиночных кавычек, поскольку компьютер посчитает её за закрывающую для первого символа.
Первые две были бы открывающей и закрывающей кавычками, а третья
была бы оставлена одна, что привело бы к ошибке компиляции.
Аналогично, поскольку символ обратной косой черты имеет особое значение в синтаксисе управляющих
символов и литералов, компилятор принял бы его за начало такого
синтаксиса: '\' . Затем компилятор будет искать закрывающий
символ одинарной кавычки, не найдет его и выдаст ошибку компиляции.
По этим причинам символы одинарной кавычки и обратной косой черты экранируются предшествующим
символом обратной косой черты:


.. list-table:: Title
   :widths: 25 25 50
   :header-rows: 1
   :align: center

   * - Синтаксис
     - Название
     - Определение
   * - \'
     - одиночная кавычка
     - Позволяет специфицировать символ одиночной кавычки
   * - \\
     - обратный слэш
     - Позволяет специфицировать символ обратный слэш

The std.uni module
------------------

Модуль std.uni включает в себя функции, полезные для работы с
символами Unicode. Вы можете ознакомиться с документацией к этому модулю.
Функции, которые начинаются с ответа на определенные вопросы о символах.
Результатом будет false или true в зависимости от того, является ли ответ соответственно "нет" или "да".


Эти функции полезны в логических выражениях:

* isLower : этот символ в нижнем регистре?
* isUpper : этот символ в вернем регистре
* isAlpha : этот символ буква (a-z или A-Z)?
* isWhite : это символ пробел? (в эту категорию входят символф пробела, табуляции и некоторые другие)

это функции преобразования символов:

* toLower : возвращает символ в нижнем регистре
* toUpper : возвращает символ в верхнем регистре


Рассмотрим пример

.. code-block:: d

    import std.stdio;
    import std.uni;

    void main()
    {
        writeln("Is ğ lowercase? ", isLower('ğ'));
        writeln("Is Ş lowercase? ", isLower('Ş'));
        writeln("Is İ uppercase? ", isUpper('İ'));
        writeln("Is ç uppercase? ", isUpper('ç'));
        writeln("Is z alphabetic? ",
            isAlpha('z'));
        writeln("Is \&euro; alphabetic? ", isAlpha('\&euro;'));
        writeln("Is new-line whitespace? ", isWhite('\n'));
        writeln("Is the underscore whitespace? ", isWhite('_'));
        writeln("The lowercase of Ğ: ", toLower('Ğ'));
        writeln("The lowercase of İ: ", toLower('İ'));
        writeln("The uppercase of ş: ", toUpper('ş'));
        writeln("The uppercase of ı: ", toUpper('ı'));
    }


Вывод

.. code-block:: console

    Is ğ lowercase? true
    Is Ş lowercase? false
    Is İ uppercase? true
    Is ç uppercase? false
    Is z alphabetic? true
    Is € alphabetic? false
    Is new-line whitespace? true
    Is the underscore whitespace? false
    The lowercase of Ğ: ğ
    The lowercase of İ: i
    The uppercase of ş: Ş
    The uppercase of ı: I


Ограниченная поддержка символов I иi
------------------------------------


Строчные и прописные буквы "I" и "i" в некоторых алфавитах (например, в турецком алфавите) последовательно разделены точками или без точек.
Большинство других алфавитов непоследовательны в этом отношении: заглавная буква пунктирной буквы "i" - это неразборчивая буква "I" .
Поскольку компьютерные системы начинались с таблицы ASCII, традиционно заглавной буквой "i" является "I", а строчной "I" - "i" . По этой причине этим
двум буквам может потребоваться особое внимание. Следующая программа демонстрирует эту проблему


.. code-block:: d

    import std.stdio;
    import std.uni;

    void main()
    {
        writeln("The uppercase of i: ", toUpper('i'));
        writeln("The lowercase of I: ", toLower('I'));
    }


Вывод

.. code-block:: console

    The uppercase of i: I
    The lowercase of I: i

Символы преобразуются между их версиями в верхнем и нижнем регистре
, как правило, с помощью кодов символов Unicode. Этот метод проблематичен для многих
алфавитов. Например, азербайджанский и кельтский алфавиты подвержены одной и той же
проблеме создания строчной буквы "I" в качестве "i" .
Аналогичные проблемы возникают и с сортировкой: многие буквы, такие как "y" и "á", могут быть
отсортированы после "z" даже для базового латинского алфавита.


Проблемы при чтении символов
----------------------------

Проблемы с чтением символов
Гибкость и мощь символов Unicode в D могут привести к неожиданным результатам
при чтении символов из входного потока. Это противоречие связано с
несколькими значениями термина character. Прежде чем продолжить обсуждение, давайте
рассмотрим программу, которая демонстрирует эту проблему:

.. code-block:: c++

   import std.stdio;

    void main()
    {
        char letter;
        write("Please enter a letter: ");
        readf(" %s", &letter);
        writeln("The letter that has been read: ", letter);
    }


Если вы попробуете эту программу в среде, которая не использует Unicode, вы можете
увидеть, что даже символы, отличные от ASCII, читаются и печатаются правильно.
С другой стороны, если вы запустите ту же программу в среде Unicode
(например, в терминале Linux), вы можете увидеть, что символ, напечатанный на выходе, не
является тем же символом, который был введен. Чтобы убедиться в этом, давайте введем
символ, отличный от ASCII, в терминале, который использует кодировку UTF-8 (как и большинство терминалов Linux).:

.. code-block:: console

    Please enter a letter: ğ
    The letter that has been read:  no letter on the output

Причина этой проблемы заключается в том, что символы, отличные от ASCII, такие как "ğ"
, представлены двумя кодами, и при чтении символа из входных данных считывается только первый
из этих кодов. Поскольку этого единственного символа недостаточно для представления всего
Символ Unicode, у программы нет полного символа для отображения.
Чтобы показать, что коды UTF-8, составляющие символ, действительно считываются по одному
символу за раз, давайте прочитаем две переменные char и выведем их одну за другой:

.. code-block:: d

    import std.stdio;

    void main()
    {
        char firstCode;
        char secondCode;
        write("Please enter a letter: ");
        readf(" %s", &firstCode);
        readf(" %s", &secondCode);
        writeln("The letter that has been read: ",
            firstCode, secondCode);
    }


Программа считывает две переменные char из входных данных и печатает их в том
же порядке, в котором они считываются. Когда эти коды отправляются на терминал в том
же порядке, они завершают кодировку UTF-8 символа Unicode на
терминале, и на этот раз символ Unicode печатается правильно:

.. code-block:: console

    Please enter a letter: ğ
    The letter that has been read: ğ


Эти результаты также связаны с тем фактом, что стандартными входными и выходными
данными программ являются потоки символов. Позже в главе о строках (стр. 75) мы увидим, что
символы легче считывать как строки, вместо того чтобы иметь дело с кодами UTF по отдельности.

Поддержка Unicode в D
----------------------

Unicode - это большой и сложный стандарт. D поддерживает очень полезное его подмножество.
Документ, закодированный в Unicode, состоит из следующих уровней понятий, от
самого нижнего до самого верхнего:

* Кодовая единица: Значения, составляющие кодировки UTF, называются кодовыми единицами.
  В зависимости от кодировки и самих символов символы Unicode состоят из одной или нескольких кодовых единиц.
  Например, в UTF-8 кодировка буквы "а" состоит из одной кодовой единицы, а буква "я" состоит из двух кодовых единиц.
  Типы символов D char , wchar и dchar соответствуют кодовым единицам UTF-8, UTF-16 и UTF-32 соответственно.

* Кодовая точка: Каждая буква, цифра, символ и т.д., определенные стандартом Unicode,
  называются кодовой точкой. Например, кодовые значения Unicode 'a' и 'ğ' являются двумя различными кодовыми точками.

В зависимости от кодировки кодовые точки представлены одной или несколькими
кодовыми единицами. Как упоминалось выше, в кодировке UTF-8 "a" представлена
одной кодовой единицей, а "ğ" представлена двумя кодовыми единицами. С другой стороны,
и "a", и "ğ" представлены одной кодовой единицей как в кодировках UTF-16, так и UTF-32.
Типом D, который поддерживает кодовые точки, является dchar . char и wchar могут
использоваться только в качестве кодовых единиц.

*Символ:* Любой символ, определенный стандартом Unicode, и то, что мы называем
"символом" или "буквой" в повседневной речи, является символом.
Это определение символа является гибким в Unicode, что приводит
к усложнению. Некоторые символы могут быть образованы более чем одной кодовой точкой.
Например, буква  ??? (турецкая кодировка как ввести) может быть указана двумя способами:

* в качестве единственной кодовой точки для "ğ"
* в качестве двух кодовых точек для "g" и "" (комбинируя breve)

Хотя для читателя-человека они означали бы один и тот же символ, одиночная
кодовая точка "ğ" отличается от двух последовательных кодовых точек "g" и "" .


Итоги
-------

* Unicode поддерживает все символы всех систем записи.
* char предназначен для кодировки UTF-8; хотя он не подходит для представления символов
  в целом, он поддерживает таблицу ASCII.
* wchar предназначен для кодировки UTF-16; хотя он не подходит для представления
  символов в целом, он может поддерживать буквы нескольких алфавитов.
* dchar предназначен для кодировки UTF-32; поскольку он имеет 32 бита, он также может представлять кодовые точки.
